home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d14 / atre12.arc / ATREE.DOC < prev    next >
Text File  |  1991-07-27  |  58KB  |  1,613 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.                           An Implementation of Adaptive Logic Networks
  13.                           ~~ ~~~~~~~~~~~~~~ ~~ ~~~~~~~~ ~~~~~ ~~~~~~~~
  14.                                   
  15.                             copyright W.W. Armstrong and Andrew Dwelly
  16.                                                      November 11, 1990
  17.  
  18.                                      bug-fixes and initial port to DOS
  19.                                                      Rolf Manderschied
  20.                                                         April 15, 1991
  21.  
  22.                                          revised for Microsoft Windows
  23.                                                       Monroe M. Thomas
  24.                                                           May 31, 1991
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72. 1     Introduction
  73. ~     ~~~~~~~~~~~~
  74.  
  75. The Windows dynamic link library atree.dll contains an implementation of
  76. an unconventional kind of learning algorithm for adaptive logic
  77. networks[Arms], which can be used in place of the backpropagation
  78. algorithm for multilayer feedforward artificial neural networks [Hech],
  79. [Rume].
  80.  
  81. The ability of a logic network to learn or adapt to produce an arbitrary
  82. boolean function specified by some empirical "training" data is certainly
  83. important for the success of the method, but there is another property of
  84. logic networks which is also essential.  It is the ability to generalize
  85. their responses to new inputs, presented after training is completed.  The
  86. successful generalization properties of these logic networks are based on
  87. the observation, backed up by a theory [Boch], that trees of two-input
  88. logic gates of types AND, OR, LEFT, and RIGHT are very insensitive to
  89. changes of their inputs.
  90.  
  91. Some experiments on handwritten numeral recognition and satellite image
  92. classification have been successfully carried out. [Arms3, Arms4].  Recent
  93. experiments have shown this algorithm to learn quickly on some problems 
  94. requiring learning of integer or continuous-valued functions where 
  95. backpropagation has reportedly led to long training times; and it 
  96. functions very well on boolean data [Arms5].
  97.  
  98. At the present time, only limited comparisons have been made with the 
  99. conventional approach to neurocomputing, so the claims necessarily have to 
  100. be muted.  This situation should rapidly be overcome as users of this 
  101. software (or improved variants of it yet to come) begin experimentation.  
  102. However one property of these networks in comparison to others is an 
  103. absolute, and will become apparent to computer scientists just by 
  104. examining the basic architecture of the networks.  Namely, when special 
  105. hardware is available, this technique, because it is based on
  106. combinational logic circuits of limited depth (e. g. 10 to 20 propagation
  107. delays), can potentially offer far greater execution speeds than other
  108. techniques which depend on floating point multiplications, additions, and
  109. computation of sigmoidal functions.
  110.  
  111. A description of the class of learning algorithms and their hardware
  112. realizations can be found in [Arms, Arms2], but we will briefly introduce
  113. the concepts here. An atree (Adaptive TREE) is a binary tree with nodes of
  114. two types: (1) adaptive elements, and (2) leaves.  Each element can
  115. operate as an AND, OR, LEFT, or RIGHT gate, depending on its state.  The
  116. state is determined by two counters which change only during training.
  117. The leaf nodes of the tree serve only to collect the inputs to the subtree
  118. of elements.  Each one takes its input bit from a boolean input vector or
  119. from the vector consisting of the complemented bits of the boolean input
  120. vector.  The tree produces a single bit as its output.
  121.  
  122.  
  123.                                     -1-
  124.  
  125. Despite the apparent limitation to boolean data, simple table-lookups permit
  126. representing non-boolean input values (integers or reals for example) as bit
  127. vectors, and these representations are concatenated and complemented to form
  128. the inputs at the leaves of the tree.  For computing non-boolean outputs,
  129. several trees are used in parallel to produce a vector of bits representing
  130. the output value.
  131.  
  132. This software contains everything needed for a programmer with knowledge of
  133. C and Windows 3.x to create, train, evaluate, and print out adaptive logic
  134. networks. It has been written for clarity rather than speed in the hope that
  135. it will aid the user in understanding the algorithms involved.  The
  136. intention was to try make this version faster than variants of the
  137. backpropagation algorithm for learning, and to offer much faster evaluation
  138. of learned functions than the standard approach given the same
  139. general-purpose computing hardware.  Users of the software are requested to
  140. provide some feedback on this point to the authors.
  141.  
  142. This software also includes a language "lf" that allows a non-programmer
  143. to conduct experiments using atrees, as well as a number of
  144. demonstrations.
  145.  
  146. A version of this software which is both faster and contains a more
  147. effective learning algorithm is planned for the near future.
  148.                                   Y
  149.                                   │
  150.                           ┌───────┴───────┐
  151.                           │  Random Walk  │
  152.                           │    Decoder    │
  153.                           └───────┬───────┘
  154.                           ┌───────┴───────┐
  155.                           │ Output Vector │
  156.                           └┬─────────────┬┘
  157.                            │             │
  158.     Trees - one per       (O)           (O)
  159.     output bit             │             │
  160.                      ┌─────┴────┐      ┌─┴─┐
  161.                  (O)─┘          └─(O)
  162.                   │                │
  163.                 ┌─┴─┐            ┌─┴─┐
  164.              (O)┘   └(O)      (O)┘   └(O)
  165.             ┌─┴─┐   ┌─┴─┐    ┌─┴─┐   ┌─┴─┐
  166.             │   │   │   │    │   │   │   │
  167.             b1 ~b1  b2 ~b1  ~b1 ~b2  b2 ~b2    Random Connections
  168.                                ┌──────┬──────┐
  169.                                │ ~b1  │ ~b2  │ Complements
  170.                                └──┬───┴──┬───┘
  171.                ┌──────────────────┘      │
  172.                │      ┌──────────────────┘
  173.             ┌──┴───┬──┴───┐
  174.             │  b1  │  b2  │ Input Vector
  175.             └──┬───┴──┬───┘
  176.           ┌────┴──────┴───┐
  177.           │  Random Walk  │
  178.           │    Encoder    │
  179.           └───┬───────┬───┘
  180.               │       │
  181.              X1       X2
  182.  
  183.          Figure 1: Using several trees to compute Y = ƒ(X1, X2)
  184.  
  185.                                     -2-
  186.  
  187. 2     Writing Applications With atree
  188. ~     ~~~~~~~ ~~~~~~~~~~~~ ~~~~ ~~~~~
  189.  
  190. Writing applications that perform a simple classification (yes or no) is
  191. relatively easy (within the constraints of Windows programming). The
  192. programmer creates a training set, then creates a tree using atree_create.
  193. The tree is trained using atree_train and then it can be used to evaluate
  194. new inputs using atree_eval. Examples of this can be seen in the files
  195. mosquito.c, and mult.c, both of which hide most of Windows' dressings
  196. for clarity.
  197.  
  198. Writing applications where the tree has to learn real number valued
  199. functions is a little more complex, as the programmer has to come to grips
  200. with the encoding problem.
  201.  
  202. Because a single tree produces only one bit, the programmer must train
  203. several trees on the input data, each one responsible for one bit of the
  204. output data. This is made slightly simpler by the choice of parameters
  205. for atree_train() which takes an array of bit vectors as the training set,
  206. and an array of bit vectors for the result set. The programmer provides
  207. an integer which states which bit column of the result set the current tree
  208. is being trained on. Typical code might look as follows:-
  209. ....
  210. {
  211.     int i;
  212.     int j;
  213.     LPBIT_VEC train;   /* LPBIT_VEC is a long (far) pointer to a bit_vec */
  214.     LPBIT_VEC result;
  215.     LPATREE *forest;   /* LPATREE is a long (far) pointer to an atree */
  216.  
  217.     /* Create the training set */
  218.     train = domain();
  219.  
  220.     /* Create the result set */
  221.     result = codomain();
  222.  
  223.     /*
  224.      * Make enough room for the set of trees - one tree per bit in the
  225.      * codomain
  226.      */
  227.     forest = (LPATREE *) malloc((unsigned)sizeof(LPATREE) * NO_OF_TREES);
  228.  
  229.     /* Now create and train each tree in turn */
  230.     for (i = 0; i < NO_OF_TREES; i++)
  231.     {
  232.         forest[i] = atree_create(variables,width);
  233.         atree_train(forest[i],train,result,i,TRAIN_SET_SIZE,
  234.                     MIN_CORRECT,MAX_EPOCHS,VERBOSITY);
  235.     }
  236.  
  237.     /*
  238.      * Where TRAIN_SET_SIZE is the number of elements in train,
  239.      * MIN_CORRECT is the minimum number of elements the tree should
  240.      * get correct before stopping, MAX_EPOCHS is the absolute maximum
  241.      * length of training and VERBOSITY controls the amount of
  242.      * diagnostic information produced.
  243.      */
  244. ......
  245.  
  246.  
  247.                                     -3-
  248.  
  249.  
  250. The standard encoding of integers into binary numbers does not work well
  251. with this algorithm since it tends to produce functions which are
  252. sensitive to the values of the least significant bit. So instead we use
  253. the routine atree_rand_walk() to produce an array of bit vectors where each
  254. vector is picked at random and is a specified Hamming distance away form
  255. the previous element. Picking the width of the encoding vector, and the
  256. size of the step in Hamming space is currently a matter of
  257. experimentation, although some theory is currently under development to
  258. guide this choice.
  259.  
  260. Real numbers are encoded by dividing the real number line into a number of
  261. quantization levels, and placing each real number to be encoded into a
  262. particular quantization. Obviously, the more quantizations there are, the
  263. more accurate the encoding will be. Essentially this procedure turns real
  264. numbers into integers for the purposes of training. The quantizations are
  265. then turned into bit vectors using the random walk technique again.
  266.  
  267. Once the trees are trained, we can evaluate them with new inputs. Despite
  268. their training, the trees may not be totally accurate, and we need some
  269. way of dealing with error. The normal approach taken is to produce a
  270. result from the set of trees, then search through the random walk for the
  271. closest bit vector. This is taken as the true result. Typical code might
  272. be as follows:-
  273.  
  274. ....
  275.     /* Continued from previous example */
  276.     int closest_elem;
  277.     int smallest_diff;
  278.     int s;
  279.     LPBIT_VEC test;
  280.     LPBIT_VEC tree_result;
  281.  
  282.     /* Now create the (single in this example) test vector */
  283.  
  284.     test = test_element();
  285.  
  286.     /* Now create some room for the tree results */
  287.  
  288.     tree_result = bv_create(No_OF_TREES);
  289.  
  290.     /* Evaluate the trees */
  291.  
  292.     for (i = 0; i < NO_OF_TREES; i++)
  293.     {
  294.         /*
  295.          * Set bit i of tree_result, the result of evaluating
  296.          * the ith tree.
  297.          */
  298.  
  299.         bv_set(i, tree_result, atree_eval(forest[i], test));
  300.     }
  301.  
  302.  
  303.  
  304.  
  305.  
  306.  
  307.  
  308.  
  309.                                     -4-
  310.  
  311.  
  312.     /*
  313.      * tree_result probably has a few bits wrong, so we will look
  314.      * for the closest element in the result array
  315.      */
  316.  
  317.     closest_elem = 0;
  318.     smallest_diff = MAX_INT;
  319.  
  320.     for (i = 0; i < TRAIN_SET_SIZE; i++)
  321.     {
  322.         if ((s = bv_diff(tree_result, result[i])) < smallest_diff)
  323.         {
  324.             smallest_diff = s;
  325.             closest_elem = i;
  326.         }
  327.     }
  328.  
  329.     /*
  330.      * At this point, result[closest_elem] is the correct bit vector,
  331.      * and smallest_diff is the amount of error produced by the tree.
  332.      */
  333.  
  334.    do_something_with(result[closest_elem]);
  335.  
  336.     /* Etc. */
  337. }
  338. ....
  339.  
  340.  
  341.  
  342.  
  343.  
  344.  
  345.  
  346.  
  347.  
  348.  
  349.  
  350.  
  351.  
  352.  
  353.  
  354.  
  355.  
  356.  
  357.  
  358.  
  359.  
  360.  
  361.  
  362.  
  363.  
  364.  
  365.  
  366.  
  367.  
  368.  
  369.  
  370.  
  371.                                     -5-
  372.  
  373.  
  374. 3     The Windows atree Library
  375. ~     ~~~~~~~~~~~ ~~~~~ ~~~~~~~
  376.  
  377. The atree library consists of a single include file atree.h, which must be
  378. included in all software making calls on the library, and a library of
  379. routines atree.dll.  The routines permit the creation, training, evaluation
  380. and testing of adaptive logic networks in a Windows environment, and there
  381. are a number of utility routines designed to make this task easier.  Note
  382. that the entire atree library has been compiled into a dynamic link library 
  383. (DLL) for use under Windows. It is only necessary to include all atree 
  384. library functions used in the IMPORTS section of the application's module
  385. definition file.  The source code for atree.dll can be found in atree.c,
  386. which can be used to compile atree routines directly into an application if
  387. desired.
  388.  
  389. The atree.dll is capable of supporting multiple instances of atree
  390. applications, although (as expected), this can slow down tree training.
  391. Mosquito.exe provides a good example of this: try clicking on the Run menu
  392. option two or three times.  Or try running mosquito.exe and mult.exe at the
  393. same time.
  394.  
  395.  
  396. 3.1   Naming Conventions
  397.  
  398. Throughout this software, the following conventions have been used :-
  399.  
  400. Publicly available functions are called atree_something(). If the routine is
  401. primarily concerned with bit vectors rather than atrees, it will be named
  402. bv_something() instead. The exceptions to this occur for functions that are
  403. directly responsible for maintaining performance of the atree software in
  404. the Windows environment.
  405.  
  406. Variables are always in lower case. The variables i, j, and k are reserved
  407. as iterators in "for" loops. The variable verbosity is reserved for
  408. controlling the amount of diagnostic information produced.
  409.  
  410.  
  411. 3.2   The Main Data Structures
  412.  
  413. The two main data structures are atree and bit_vec which represent atrees
  414. and bit vectors respectively. They are both fully specified in the file
  415. atree.h.
  416.  
  417. Examining the structure atree first, we find a recursive structure which
  418. represents a single node in an atree. Since it is binary, it consists of
  419. the data which the node holds, and two pointers to child nodes. If the
  420. subnode is a leaf of the tree it will contain a bit number rather than a
  421. pointer, so both left and right are unions of pointers and integers to
  422. represent these two possibilities.
  423.  
  424. The internal data of the node consists of a series of boolean flags
  425. (represented by chars here), a char which describes the function of the node
  426. (AND, OR, LEFT, or RIGHT), two chars for signals from the child nodes, and
  427. two counters cnt_10 and cnt_01 used during training.
  428.  
  429.  
  430.  
  431.  
  432.  
  433.                                     -6-
  434.  
  435.  
  436. Leaf_flag actually represents two flags, and each nibble of the byte is set 
  437. to true if the left (or respectively, right) input of the node is a leaf 
  438. node, which represents a "lead" to be connected to a bit of the input vector 
  439. or its complement, rather than to an adaptive element.  The left and right 
  440. nibbles of the flag cmp_flag are only meaningful if the corresponding nibble 
  441. in leaf_flag is true. They represent the fact that the value of the bit the 
  442. leaf accesses is complemented before it is input into the tree. The flag 
  443. seg_flag is used to mark nodes that are at the beginning of segments (offset
  444. 0), since these nodes will begin a new dynamically allocated global heap
  445. segment.
  446.  
  447. Two important type definitions exist to facilitate access to the main data
  448. structures. LPBIT_VEC is typedef'd as "bit_vec far *" (a long pointer to a 
  449. bit_vec). All bit_vec data structures should be declared with LPBIT_VEC.  
  450. Similarly, LPATREE is typedef'd as "atree far *" (a long pointer to an
  451. atree).  All atree data structures should be declared with LPATREE.
  452.  
  453.  
  454. 3.3   Manifest Constants
  455.  
  456. All the constants defined are internal to atree.c and are declared at the top
  457. of this file.
  458.  
  459. Constants AND, OR, LEFT, RIGHT represent the current function of a node.  
  460. LEFTLEAF and RIGHTLEAF are used as masks for leaf_flag TRUE and FALSE are 
  461. used to represent boolean values both in program flags and also in the tree 
  462. itself.  We also include UNEVALUATED and ATREE_ERROR in the boolean type. 
  463. These complete the type lattice for booleans, but more importantly allow us
  464. to signal that a subtree has not been evaluated during training, or is in
  465. error.
  466.  
  467. The constant MAX_RETRY is used in the random walk routines to control the
  468. maximum amount of searching for a bit vector than has not previously been
  469. encountered.
  470.  
  471. The constants MAXSET, ABOVEMID, BELOWMID, and MINSET are used during the 
  472. initialisation of a tree.  The operation of a node in the tree is determined 
  473. by the value of its counters, the char atree.function in a node is 
  474. determined by them. The above constants represent the full range of a
  475. counter and also mark its middle values.
  476.  
  477. The constants SEGLENGTH and NUMSEGS are used by the atree memory mangement 
  478. routines.  SEGLENGTH defines the number of bytes in a data segment; NUMSEGS 
  479. defines the maximum number of dynamically allocated data segments allowed.  
  480. Note that bit vectors are allocated using the atree memory management
  481. routines but that atrees themselves are allocated using Windows' global
  482. memory routines, so the NUMSEGS restriction does not apply to trees.
  483.  
  484. 3.4   Private Macros
  485.  
  486. The following macros are private to atree.c.
  487.  
  488. The macro Printf serves as a nice front end for the Windows API version of
  489. sprintf, wsprintf.
  490.  
  491. The terms public and private are used to denote whether a routine is for
  492. use outside the package (public) or is strictly internal (private).  They
  493. take advantage of the fact that in C, a routine that is declared as static
  494. may not be accessed outside its source file.
  495.  
  496.  
  497.  
  498. The macro EVER is a standard C trick used to describe an infinite loop
  499. typically it will be used with a for as in for EVER.
  500.  
  501. In order to print out a boolean value (in our raised definition of
  502. booleans) we define the macro PRINTBOOL.
  503.  
  504. The macro VERBOSE is used to control the amount of diagnostic information
  505. produced by a program. The variable verbosity is set to a particular level,
  506. and if the statement s is associated with that or a lower level, it is
  507. executed. Typically we will see the following sort of usage
  508. VERBOSITY(1,diagnostic());  diagnostic() will be executed, providing the
  509. verbosity level is greater or equal to 1.
  510.  
  511. The macro BYTE gives the size (in bits) of a byte on this machine. (OK,
  512. this is a hold over from the UNIX version!)
  513.  
  514. The macros EVAL, LEFTEVAL,and RIGHTEVAL are used during tree evaluation.
  515. They will be explained in detail in the routines that call them.
  516.  
  517.  
  518. 3.5   Public Macros
  519.  
  520. The following macros are defined in atree.h and are available to any
  521. application using the atree library.
  522.  
  523. The macro MEMCHECK allows us to check the validity of a pointer.  For
  524. example, if the pointer p in MEMCHECK(p) is NULL, then a message box pops up
  525. with appropriate notification, and the application is terminated.
  526.  
  527. The macro RANDOM allows us to conveniently produce a random number between 0
  528. and some user-specified x in the program. For example, in order to produce a
  529. random true or false value (0 or 1) we write RANDOM(2).
  530.  
  531. The macro Malloc serves as a front end for the atree memory allocation
  532. routine WinMem_Malloc().  To allocate a chunk of 16 bytes to a pointer p,
  533. use p = Malloc(16).
  534.  
  535. The macro Free serves as a front end for the atree memory routine
  536. WinMem_Free().  To free the memory pointed by a pointer p that was allocated
  537. with WinMem_Malloc() (or the macro Malloc), use Free(p).
  538.  
  539.  
  540.  
  541. 3.6   Global Variables in atree.c
  542.  
  543. The global variables seg and freemem are used by the atree memory
  544. management routines to maintain dynamically allocated data segments, and are
  545. private to atree.c
  546.  
  547.  
  548.  
  549.  
  550.  
  551.  
  552.  
  553.  
  554.  
  555.  
  556.  
  557.                                     -8-
  558.  
  559.  
  560. 3.7   The Windows atree API
  561.  
  562. The following routines are available to your application when developing
  563. atree applications.  Note that they are all available through atree.dll, so
  564. the atree library functions do not need to be linked into an application.
  565. Instead, simply include the needed atree library functions in the
  566. application module definition file (see mosquito.def for an example).
  567. Should the programmer choose, atree.c can be compiled and linked directly
  568. into your application.  If this is done, make sure the LibMain() function in
  569. atree.c (see section 3.8.1) is commented out, as it serves as the DLL entry
  570. point for the library.
  571.  
  572.  
  573. 3.7.1   void atree_init()
  574.  
  575. This routine should be called by the user before making calls to any other
  576. atree library routine.  Currently, it merely calls the srand() routine to
  577. initialize the random number generator, but it may do more in future
  578. versions.
  579.  
  580.  
  581. 3.7.2  LPBIT_VEC atree_rand_walk(num,width,p)
  582.  
  583. int num;
  584. int width;
  585. int p;
  586.  
  587. The standard encoding of integers into binary is not suitable for adaptive 
  588. logic networks, since the least significant bits vary quickly during 
  589. incrementations of the integer compared to the more significant bits. The 
  590. effect of binary number encoding is easy to see when we consider the result 
  591. of a single bit error occurring in the output of a collection of trees (a 
  592. forest): how important the error is depends on the position of the bit in 
  593. the output vector. An error in the least significant bit of the vector makes 
  594. a difference of one unit in the output integer; an error in the most
  595. significant bit causes a large difference in the output integer depending on
  596. the width of the vector.
  597.  
  598. A better encoding is one where each bit varies at about the same rate; and 
  599. we can create such an encoding by taking a random walk through Hamming space
  600. [Smit].  A randomly produced vector is chosen to represent the first integer
  601. value in a sequence.  For each subsequent integer, a specified number of
  602. bits, picked a random, are changed to create the next vector.
  603.  
  604. The routine atree_rand_walk() does this job, with the additional guarantee 
  605. that each vector produced is unique. The parameter num gives the number of 
  606. vectors, or "steps" in the walk, required, the parameter width gives the
  607. width in bits of each vector, and the parameter p is the distance of each
  608. step in the Hamming metric (the number of bits which change).
  609.  
  610. The uniqueness requirement makes the routine rather more complex than one
  611. might expect. Because we expect to be using large random walks, it was felt
  612. that a simple check against all the previously created vectors would not be
  613. efficient enough. Instead all vectors with the same weight (the weight of a
  614. bit vector is the number of 1s in it; e. g., the weight of 10110 is 3) are
  615. chained together, and only those vectors with a weight equal to the one
  616. currently being checked for uniqueness are examined.  If the vector is not
  617.  
  618.  
  619.                                     -9-
  620.  
  621.  
  622. unique, the routine will go back to the previous unique vector and try
  623. randomly changing some other bits. In order to avoid an infinite loop, it
  624. will only try MAX_RETRY times to do this. If it cannot proceed, the routine
  625. aborts.  A better version of the software would check to assure a minimum
  626. distance between points.
  627.  
  628.  
  629. 3.7.3   public LPATREE atree_create(numvars,leaves)
  630.  
  631. int numvars;
  632. int leaves;
  633.  
  634. This is the routine used to create an atree of a given size. The parameter
  635. leaves gives the number of leaves or output leads to the tree, and hence
  636. controls its size, which is one less than this.  A balanced tree is chosen
  637. if possible.
  638.  
  639. The parameter numvars is the number of boolean variables in the bit vector
  640. input to the tree.  It is used during initialization of the (random)
  641. connections between leaf nodes of the tree and the input bit vector. Usually
  642. the bits of the input vector, and their complements will be required as
  643. inputs to the tree since there are no NOT nodes in the tree itself. It is
  644. therefore recommended that there be at least twice as many inputs to the
  645. tree as there are bits in the input vector for a given problem:
  646.  
  647.                             leaves >= 2 * numvars
  648.  
  649. The routine itself proceeds by deciding which bit of the input vector
  650. is to be connected to each leaf, and stores the information in two
  651. arrays connection which holds the bit numbers, and
  652. complemented which shows whether the connection is complemented or
  653. not.  It then calls a private recursive tree-building routine
  654. build_tree().  The latter routine depends on having enough space
  655. already allocated on the heap and atree_create() is responsible
  656. for that.
  657.  
  658.  
  659. 3.7.4  public BOOL atree_eval(tree,vec)
  660.  
  661. LPATREE tree;
  662. LPBIT_VEC vec;
  663.  
  664. This routine is responsible for calculating the output of a tree from a 
  665. given bit vector. It takes advantage of the standard C definition of && and
  666. || to do this in the required parsimonious(1) fashion [Meis][Arms5].  The
  667. Macro LEFTEVAL is responsible for evaluating the left subtree and RIGHTEVAL
  668. is responsible for the right subtree. They both use the EVAL macro which is
  669. a little complex since it has to check whether or not a node is a leaf and
  670. is connected to the input bit-vector, and if it is, whether the value is to
  671. be inverted or not.
  672.  
  673. This routine also marks subtrees that are unevaluated, and sets the internal
  674. atree.sig_left and atree.sig_right values for a node. This information is
  675. used when atree_eval() is used from within atree_train.
  676.  
  677. _______
  678. (1) I really don't like this word - it makes me think of Scrooge (A.D.).
  679. However, if you really had to pay for massive parallelism rather than
  680. parsimonious parallelism, I suppose you could be persuaded to like the term
  681. (W.A.).  No I couldn't (A.D.).
  682.  
  683.  
  684. 3.7.5  public BOOL atree_train(tree,tset,...)
  685.  
  686. LPATREE tree
  687. LPBIT_VEC tset;
  688. LPBIT_VEC correct_result;
  689. int bit_col;
  690. int tset_size;
  691. int no_correct;
  692. int epochs;
  693. int verbosity;
  694.  
  695. atree_train() is the routine that adapts a tree to learn a particular
  696. function.  It is a little more complex than you might expect as it has been
  697. arranged to make it convenient to train multiple trees on the same training
  698. set.
  699.  
  700. The parameter tree is the tree to be trained, and the parameter tset is the
  701. array of bit vectors which the tree is to be trained on (the training set).
  702. An atree only produces a single bit, so in principle all that is needed for
  703. the correct_result parameter is an array of bits, with one bit corresponding
  704. to each bit vector in the training set. In training multiple trees (when
  705. learning a quantized real-valued function, for example), it is more
  706. convenient to keep the correct results in an array of bit vectors, and
  707. specify which column of the array a tree is supposed to be learning. This is
  708. the purpose of the array correct_result and the integer bit_col.
  709.  
  710. The next parameter tset_size gives the number of elements in tset and 
  711. correct_result (which have to be the same --- there must be a result for 
  712. every input to the function).
  713.  
  714. The next two parameters control the amount of training that is to be done.  
  715. We train on the vectors of the training set in pseudo-random order.  The 
  716. term epoch here is used to mean a number of input vector presentations equal 
  717. to the size of the training set.  The parameter epochs states how many
  718. epochs may be completed before training halts.  The parameter no_correct 
  719. states how many elements in the training set the tree must get correct 
  720. during an epoch before training halts.  The routine will therefore stop at 
  721. whichever of these two conditions is true first. For example given that we 
  722. have a training set with 10 elements and we wish to train for 15 epochs or 
  723. until 90% of the elements presented during an epoch have been responded to 
  724. correctly. We can achieve this by setting no_correct to 9 and epochs to 15.
  725.  
  726. The verbosity parameter controls how much diagnostic information the routine 
  727. will produce. At the moment only 0 (silent) or 1 (progress information) is 
  728. implemented.  The progress information consists of popup message boxes which 
  729. require a user click on an "OK" button to continue (Future versions of the 
  730. software will have better progress information handling, which will not
  731. require user supervision).
  732.  
  733. The routine decides which vector is the next to be presented to the tree and 
  734. extracts the result bit from the correct_result array. It also keeps track 
  735. of the number of epochs, and the number of correct responses from the tree. 
  736. The process of training is done by the private train() routine.
  737.  
  738.  
  739.  
  740.  
  741.  
  742.  
  743.                                     -11-
  744.  
  745.  
  746. 3.7.6  public void atree_print(tree,verbosity)
  747.  
  748. LPATREE tree;
  749. int verbosity;
  750.  
  751. This routine allows the programmer to output an atree to disk before,
  752. during, or after training, in a form suitable for printing. The parameter
  753. tree is the tree to be printed, and verbosity is the amount of information
  754. produced.  The disk file is currently hard coded as "atree.out" (future
  755. versions of the software will allow user selected output streams).
  756.  
  757. The routine makes an immediate call to the private print_tree routine.
  758.  
  759.  
  760. 3.7.7  public void atree_free(tree)
  761.  
  762. LAPTREE tree;
  763.  
  764. This routine frees up the atree pointed to by tree.  It descends the
  765. structure, searching for nodes that are the beginning of new segment blocks,
  766. as indicated by tree->seg_flag.
  767.  
  768.  
  769. 3.7.8  public LPBIT_VEC bv_create(length)
  770.  
  771. int length;
  772.  
  773. Creates a vector of length bits, where each bit is initialised to 0, and
  774. returns a long pointer to the bit vector.
  775.  
  776.  
  777. 3.7.9  public LPBIT_VEC bv_pack(unpacked,length)
  778.  
  779. LPSTR unpacked;   (LPSTR is Windows for "char far *")
  780. int length;
  781.  
  782. This routine has been provided to make it easy for the programmer to produce
  783. bit vectors. The routine is handed an array of characters containing the
  784. value 0 or 1 (unpacked) and an integer length giving the number of bits. The
  785. routine returns a long pointer to a bit_vec.
  786.  
  787.  
  788. 3.7.10  public int bv_diff(v1,v2)
  789.  
  790. LPBIT_VEC v1;
  791. LPBIT_VEC v2;
  792.  
  793. This routine calculates the Hamming distance between v1 and v2, i.e.
  794.  
  795.                         weight (v1 XOR v2)
  796.  
  797. where weight is the number of one bits in a vector and XOR is the bitwise
  798. exclusive-or operation. This routine is used to find the closest vector in a
  799. random walk array to some arbitrary vector. Just search through the random
  800. walk for the vector with the smallest difference from the vector of tree
  801. output bits.  (Inefficient, but easier to understand than decoding an
  802. algebraic code!).
  803.  
  804.  
  805.                                     -12-
  806.  
  807.  
  808. 3.7.11  public LPBIT_VEC bv_concat(n,vectors)
  809.  
  810. int n;
  811. LPBIT_VEC far *vectors;
  812.  
  813. This routine is used by the programmer to join several bit vectors
  814. end-to-end to give the string concatenation of the vectors. This routine is
  815. most frequently used during the construction of training sets when elements
  816. of several random walks have to be joined together to obtain an input vector
  817. to a tree.
  818.  
  819. The parameter vectors is an array of bit_vec pointers, and the parameter n 
  820. states how many of them there are. Vector pointers are used to make this
  821. routine a little faster since there is less copying involved.  A long
  822. pointer to the concatenated bit_vec is returned.
  823.  
  824.  
  825. 3.7.12  public void bv_print(stream, vector)
  826.  
  827. FILE *stream;
  828. LPBIT_VEC vector;
  829.  
  830. This is a diagnostic routine used to print out a bit_vec.
  831.  
  832.  
  833. 3.7.13  public void bv_set(n,vec,bit)
  834.  
  835. int n;
  836. LPBIT_VEC vec;
  837. BOOL bit;
  838.  
  839. This routine allows the programmer to explicitly set (or reset) the nth bit
  840. (0 to bit_vec.len - 1) bit in the vector vec to have the value in the
  841. parameter `bit'.
  842.  
  843.  
  844. 3.7.14  public BOOL bv_extract(n,vec)
  845.  
  846. int n;
  847. LPBIT_VEC vec;
  848.  
  849. This routine returns the value of the nth bit (0 to bit_vec.len - 1) in the 
  850. bit vector vec. The rather unpleasant expression works as follows :-
  851.  
  852. The parameter n is divided by eight to get the number of the byte where the
  853. bit is held. This number is added to the first byte to get the actual
  854. address of the byte concerned.
  855.  
  856. The remainder of the division n % BYTE is used to find where in the
  857. byte, the bit is. A mask is shifted left this number of times and logically
  858. and-ed with the byte. If the result is 0 the bit was 0. If the result
  859. is greater than one, the bit was 1. The test for equality to zero forces the
  860. result to be just 1 or 0.
  861.  
  862.  
  863.  
  864.  
  865.  
  866.  
  867.                                     -13-
  868.  
  869.  
  870. 3.7.15  public BOOL bv_equal(v1,v2)
  871.  
  872. LPBIT_VEC v1;
  873. LPBIT_VEC v2;
  874.  
  875. This routine tests two bit vectors for equality.
  876.  
  877.  
  878. 3.7.16  public void bv_free(vector)
  879.  
  880. LPBIT_VEC vector;
  881.  
  882. This routine frees the memory used by a bit_vec, accessing a bit_vec after
  883. it has been freed is usually disastrous.
  884.  
  885.  
  886. 3.7.17  public void Windows_Interrupt(cElapsed)
  887.  
  888. DWORD cElapsed;     (DWORD is Windows for "unsigned long")
  889.  
  890. When called, this procedure allows Windows to multitask an atree application 
  891. with other Windows applications. This is accomplished with a PeekMessage() 
  892. call (see the Windows Programmer's Reference for more details). The 
  893. programmer may want to use this procedure during long tree evaluation and 
  894. training set generation loops, or during other processing where control may 
  895. not be passed back to the application's window procedure for lengthy periods 
  896. of time (the price you pay for non-preemptive multitasking!).  Since 
  897. PeekMessage() calls can be quite time consuming, this procedure will only 
  898. call PeekMessage() after cElapsed milliseconds have passed since the last 
  899. call to PeekMessage().  Experimentation has shown a value for cElapsed of 
  900. about 1500 to work fairly well.
  901.  
  902.  
  903. 3.7.18  public LPSTR WinMem_Malloc(wFlags, wBytes)
  904.  
  905. WORD wFlags;    (WORD is Windows for "unsigned int(16-bit)")
  906. WORD wBytes;
  907.  
  908. Since the segmented memory architecture of DOS based PC's can cause great 
  909. grief when allocating large amounts of memory, the atree package includes 
  910. its own memory manager.  Requests for memory are obtained from dynamically 
  911. allocated segments from the global heap in which local heaps have been 
  912. initialized.  The memory is actually allocated by Windows' local heap 
  913. manager, and the resultant near (16 bit) pointer is combined with the global 
  914. segment descriptor to form a long (32 bit) pointer suitable for use in 
  915. Windows applications.  wFlags indicate the kind of memory to allocate, 
  916. usually LMEM_MOVEABLE, and wBytes indicate the number of bytes to allocate.  
  917. See the Windows Programmer's Reference LocalAlloc() routine for more 
  918. information on the different values wFlags may take on.  For ease of use, 
  919. the programmer may simply wish to use the Malloc(wBytes) macro, which
  920. expands to WinMem_Malloc(LMEM_MOVEABLE, wBytes).
  921.  
  922.  
  923.  
  924.  
  925.  
  926.  
  927.  
  928.  
  929.                                     -14-
  930.  
  931.  
  932. 3.7.19 public LPSTR WinMem_Free(lpfree)
  933.  
  934. LPSTR lpfree;
  935.  
  936. This function frees the block of memory pointed to by lpfree, which is
  937. decomposed into a segment selector, which is used to identify the global
  938. segment from which the near pointer was allocated from, and a near pointer,
  939. which is used by Windows' LocalFree() to free memory from the local heap in
  940. the dynamically allocated segment.  If there remains no more allocated
  941. memory in the local heap(indicated by the freemem variable, the global 
  942. segment is deallocated.  For ease of use, the Free(lp) macro expands to 
  943. WinMem_Free((LPSTR) lp).
  944.  
  945. The function returns NULL if successful, otherwise it returns lpfree.
  946.  
  947.  
  948. 3.8   Private atree Routines
  949.  
  950. The following routines are internal to the atree software, and cannot be
  951. called by atree applications.
  952.  
  953.  
  954. 3.8.1  int LibMain(hInstance, wDataSeg, wHeapSize, lpzsCmdLine)
  955.  
  956. HANDLE hInstance;
  957. WORD wDataSeg;
  958. WORD wHeapSize;
  959. LPSTR lpszCmdLine;
  960.  
  961. This routine serves as the entry point for the DLL version af the atree 
  962. software.  It should be commented out if the programmer wishes to compile 
  963. and link atree.c directly with an atree application.
  964.  
  965.  
  966. 3.8.2  private void WinMem_Init()
  967.  
  968. This routine initializes the atree memory manager, and is called the first
  969. time atree.dll is loaded.
  970.  
  971.  
  972. 3.8.3  private LPATREE build_tree(connection,....)
  973.  
  974. int *connection;
  975. bool *complemented;
  976. int start;
  977. int end;
  978. LPATREE tree;
  979.  
  980. This is the recursive routine that does most of the work when creating an
  981. atree. It has an array of leaf-to-bit-vector connections (connection) and
  982. another array telling it which of the connections are to be inverted
  983. (complemented). It has a start and an end parameter which mark the part of
  984. the connection array this subtree is connected to.
  985.  
  986. The routine allocates a single node, and then makes a decision based on the
  987. size of the remaining subtree (which it knows from the start and end
  988. parameters). The simplest case is when there is a difference of four or more
  989. between start and end. Under these circumstances the routine recursively
  990.  
  991.                                     -15-
  992.  
  993.  
  994. calls itself to allocate the left and right subtrees. The left subtree is
  995. allocated immediately after the current node, and the right subtree is
  996. allocated after the left subtree. It is able to do this since build_tree()
  997. always returns the next available space for node allocation.
  998.  
  999. If the difference between start and end is less than four, the routine 
  1000. allocates some leaves using the information in the connection and 
  1001. complemented arrays to specify how the input vector is to be accessed.
  1002.  
  1003.  
  1004. 3.8.4  private void print_tree(tree,indent,verbosity,hOut)
  1005.  
  1006. LPATREE tree;
  1007. int indent;
  1008. int verbosity;
  1009. int hOut;
  1010.  
  1011. This routine does most of the work of printing out trees. It recursively 
  1012. calls itself with a larger "indent" to print out the rightmost subtree, then 
  1013. it prints out information about the current node, then recursively calls 
  1014. itself again to print out the right subtree. This particular order was 
  1015. chosen so that if the printout is tipped onto its side, it will resemble the
  1016. usual diagram of a tree.
  1017.  
  1018. The verbosity can currently be set to 0 (tree structure) or 1 (signal
  1019. values).
  1020.  
  1021. hOut is the internal file handle used to access atree.out.
  1022.  
  1023.  
  1024. 3.8.5  private bool train(tree,vec,result)
  1025.  
  1026. LPATREE tree;
  1027. LPBIT_VEC vec;
  1028. bool result;
  1029.  
  1030. This routine trains a particular to return the given result
  1031. when it sees the given bit vector vec. It does this by working out the
  1032. current response of the tree to the vector using atree_eval() then
  1033. adapting the tree using the private adapt() routine.
  1034.  
  1035.  
  1036. 3.8.6  private void adapt(tree,vec,result)
  1037.  
  1038. LPATREE tree;
  1039. LPBIT_VEC vec;
  1040. bool result;
  1041.  
  1042. This routine contains the heuristic responsibility algorithm. We define two
  1043. macros which are to be used locally, INCR and DECR, they are used to
  1044. increment and decrement the atree.cnt_10 and atree.cnt_01 counters which are
  1045. bounded by the constants MAXSET and MINSET.
  1046.  
  1047. The adaptation algorithm works its way recursively down the tree changing
  1048. the counters on nodes that are determined to be "heuristically responsible".
  1049. If a node is determined to be heuristically responsible, the algorithm
  1050. requires the evaluation of unevaluated subtrees to proceed --- this may not
  1051.  
  1052.  
  1053.                                     -16-
  1054.  
  1055.  
  1056. have been completed by the evaluation stage of train() because of
  1057. parsimony(2). So the left and right subtrees are evaluated at this stage if
  1058. necessary.
  1059.  
  1060. The next stage is to update one of the counters; if the left subtree has the
  1061. value 1 and the right subtree has value 0, then the cnt_10 counter is the
  1062. one changed.  If the desired result of the tree is 1, it is incremented,
  1063. otherwise it is decremented.  The other counter cnt_01 is left unchanged,
  1064. since it only changes when the left subtree is 0 and the right subtree is 1.
  1065. The function value of the node may now have changed, so this is recomputed.
  1066.  
  1067. Finally, a fairly complex condition is used to decide whether either the
  1068. left or right subtree, or both, are heuristically responsible and thus
  1069. should be adapted in turn. The source code is the most concise definition of
  1070. this and it is recommended that you examine the code directly.  The
  1071. heuristics used are intended to solve the "credit assignment problem", i.e.
  1072. they determine which nodes are responsible for the correct or incorrect
  1073. result of the tree.  The success of these heuristics depends a lot on the
  1074. fact that the allowed node functions are monotonic.  Finding good heuristics
  1075. for assigning responsibility is the most difficult question in connection
  1076. with using adaptive logic networks.
  1077.  
  1078. 3.8.7  private char node_function(tree)
  1079.  
  1080. LPATREE tree;
  1081.  
  1082. This routine determines the action of a node (AND, OR, LEFT, RIGHT) from the
  1083. value of its counters.
  1084.  
  1085. 3.8.8  error()
  1086.  
  1087. This is a last ditch routine called by atree_rand_walk() if it
  1088. can not proceed further.
  1089.  
  1090.  
  1091.  
  1092.  
  1093.  
  1094.  
  1095.  
  1096.  
  1097.  
  1098.  
  1099.  
  1100.  
  1101.  
  1102.  
  1103.  
  1104.  
  1105.  
  1106.  
  1107.  
  1108.  
  1109.  
  1110.  
  1111.  
  1112. _______
  1113. (2) laziness!
  1114.  
  1115.                                     -17-
  1116.  
  1117.  
  1118. 4     The Language lf
  1119. ~     ~~~ ~~~~~~~~ ~~
  1120.  
  1121. The second major product included in the current release is the "lf"
  1122. language interpreter that allows a non-programmer to experiment with tree
  1123. adaptation.  The user specifies a training set, and a test set, and selects
  1124. the encoding and quantization levels for a particular experiment. The
  1125. interpreter checks the statements for errors then executes the desired
  1126. experiment, finally outputting a table comparing the desired function with
  1127. the function actually learned. Various post-processors can use the
  1128. information to produce histograms of error or plots of the functions.
  1129.  
  1130. It is recommended that the user read and understand [Arms5] before using this
  1131. language.
  1132.  
  1133. There are two versions of lf: LF.EXE and LFEDIT.EXE.  LF.EXE inputs a file
  1134. "lf.in" and outputs to a file "lf.out".  LFEDIT.EXE is an interactive
  1135. editor, but can only handle files of about 48K.  Use LF.EXE to test SPHERE.LF.
  1136.  
  1137.  
  1138. 4.1  multiply.lf
  1139.  
  1140. The language is best learned by examining an example. The file multiply.lf
  1141. contains a simple experiment where we are trying to teach the system the
  1142. multiplication table. The program is divided into a "tree" section which 
  1143. describes the tree and the length of training, and a "function" section 
  1144. which describes the data to be learned. Comments are started with a `#' mark
  1145. and continue to the end of the line.
  1146.  
  1147. # A comment.
  1148. tree
  1149.         size = 4000
  1150.         min correct  = 143
  1151.         max epochs  = 20
  1152.  
  1153. The tree and function sections can be in any order, in this particular 
  1154. example the tree is described first. Apart from comments, tabs and newlines 
  1155. are not significant; the arrangement chosen above is only for readability.  
  1156. The first line after tree tells the system how large the atree is going to 
  1157. be. In this case we are choosing a tree with 4000 leaves (3999 nodes). We 
  1158. are going to train it until it gets 143 correct from the training set, or 
  1159. for 20 complete presentations of the training set, whichever comes first.
  1160.  
  1161. The statements in the tree section may be in any order.
  1162.  
  1163. function
  1164.         domain dimension = 2
  1165.         coding = 32:12 32:12 32:7
  1166.         quantization = 12 12 144
  1167.         training set size = 144
  1168.         training set =
  1169.  
  1170.  
  1171. 1       1       1
  1172. 1       2       2
  1173. 1       3       3
  1174. 1       4       4
  1175. ....
  1176.  
  1177.                                     -18-
  1178.  
  1179.  
  1180.         test set size = 144
  1181.         test set =
  1182.  
  1183. 1       1       1
  1184. 1       2       2
  1185. 1       3       3
  1186. 1       4       4
  1187. ....
  1188.  
  1189. The training set MUST start with a dimension statement which gives the 
  1190. number of columns in the function table. Currently the codomain size is 
  1191. restricted to one so we are defining a problem with 3 columns --- 2 input 
  1192. and one output.
  1193.  
  1194. The other statements may come in any order; note however that the definition 
  1195. of the training set size must be defined before the training set. This also 
  1196. applies to the test set definition.
  1197.  
  1198. The coding statement defines is a series of <width>:<step> definitions, one 
  1199. for each column. The <width> is the number of bits in the bit vector for 
  1200. that column, the <step> is the step size of the walk in Hamming space that 
  1201. defines the encoding of this column. Because a tree only produces a single
  1202. bit in response to an input vector, the <width> of the codomain column (the
  1203. last one) actually defines how many trees will be learning output bits of
  1204. this function.
  1205.  
  1206. The quantization statement defines for each column, the total number of 
  1207. coded bit vectors for that column. Entries in the test and training sets are
  1208. encoded into the nearest step, so this statement defines the accuracy
  1209. possible.
  1210.  
  1211. If a particular column is has a coding entry 1:1, it is treated as a special 
  1212. case, a boolean column. Only values of 0, representing false, and 1, 
  1213. representing true, make any sense in this column (although this is not
  1214. currently checked).
  1215.  
  1216. The training set statement defines the actual function to be learned by the 
  1217. system. An entry in a table can be either a real number or an integer at the 
  1218. moment.  Boolean values are special cases of integers.
  1219.  
  1220. The test set statement defines the test that is run on the trees at the end 
  1221. of training to see how well the learned function performs. Like the test 
  1222. set, reals or integers are acceptable.
  1223.  
  1224. After lf has executed, it produces a table of output showing how each 
  1225. element in the test set was quantized, and the value the trained tree 
  1226. returned.  Consider the following results that multiply.lf produced.
  1227.  
  1228. .....
  1229. 3.000000 2      11.000000 10    33.000000 32    32.777779 32
  1230. 3.000000 2      12.000000 11    36.000000 35    35.756945 35
  1231. 4.000000 3      1.000000 0      4.000000 3      3.979167 3
  1232. 4.000000 3      2.000000 1      8.000000 7      7.951389 7
  1233. 4.000000 3      3.000000 2      12.000000 11    11.923611 11
  1234. .....
  1235.  
  1236. Each column consists of two numbers, the entry specified by the user, and
  1237. an integer describing the quantization level it was coded into.
  1238.  
  1239.                                     -19-
  1240.  
  1241.  
  1242. The fourth column is the result produced by the trained tree.  It shows the
  1243. quantization level produced (the second figure) and how this may be
  1244. interpreted in the space of the codomain (the first figure).
  1245.  
  1246.  
  1247. 4.2  sphere.lf
  1248.  
  1249. This lf example uses a spherical harmonic function Y2 defined by:
  1250.  
  1251.             Y2(µ, φ)  =  A0(3µ²/2 - 1/2)
  1252.                        + 3µ(1 - µ²)^½ [A1 cos φ + B1 sin φ]
  1253.                        + 3(1 - µ²) [A2 cos 2φ + B2 sin 2φ]
  1254.  
  1255. where A0 = 1.0, A1 = 0.4, B1 = 0.9, A2 = 2.4, B2 = 7.9.  The values of µ 
  1256. were in the interval [0.0, 1.0], and the values of φ were in [0.0, π].  The
  1257. values of Y2 range between -26.0 and 26.0.
  1258.  
  1259. The µ and φ intervals were quantized into 100 levels each; the random walks
  1260. had 64 bits and a stepsize of 3.  The Y2 values were quantized into 100
  1261. levels, the random walk having 64 bits with a stepsize of 3.  Training 64
  1262. networks of 8191 elements on 1000 samples resulted in a function which,
  1263. during test on 1000 new samples, was decoded to the correct quantization
  1264. level, plus or minus three, 88.6\% of the time.  The error in the quantized
  1265. result was no more than nine quantization levels for all of the test
  1266. samples.  (A slightly better learning algorithm got within three levels
  1267. 95.8\% of the time, and was always within eight levels.)
  1268.  
  1269. The function section introduces the optional "largest" and "smallest" 
  1270. statements.  These may be used if the user needs to explicitly define the 
  1271. largest and smallest values in the test and training sets. If they are 
  1272. missing, lf will just use the largest and smallest values for each column in
  1273. both the test and training sets.
  1274.  
  1275. This problem takes about 80 minutes of CPU time on a Sun Sparcstation 1.  We
  1276. have included a sample set of results in the file sphere.out.
  1277.  
  1278.  
  1279. 4.3  The Syntax of lf
  1280. ~~~  ~~~ ~~~~~~ ~~ ~~
  1281.  
  1282. The syntax has been defined using YACC. Tokens have been written in quotes 
  1283. to distinguish them. Note that the following tokens are synonyms :-
  1284.  
  1285. dimension, dimensions
  1286. max, maximum
  1287. min, minimum
  1288.  
  1289. The syntax is defined as follows :-
  1290.  
  1291. program : function_spec tree_spec
  1292.         | tree_spec function_spec
  1293.  
  1294. function_spec : "function" dimension function_statements
  1295.  
  1296. dimension : "domain dimension =" integer
  1297.  
  1298. function_statements : function_statement
  1299.                     | function_statements function_statement
  1300.  
  1301.                                     -20-
  1302.  
  1303.  
  1304. function_statement : quantization
  1305.                    | coding
  1306.                    | train_table_size
  1307.                    | train_table
  1308.                    | test_table_size
  1309.                    | test_table
  1310.                    | largest
  1311.                    | smallest
  1312.  
  1313. quantization : "quantization =" quant_list
  1314.  
  1315. quant_list : integer
  1316.            | quant_list  integer
  1317.  
  1318. coding : "coding =" code_list 
  1319.  
  1320. code_list : integer ":" integer
  1321.           | code_list integer ":" integer
  1322.  
  1323. train_table_size : "training set size =" integer
  1324.  
  1325. train_table : "training set =" table
  1326.  
  1327. test_table_size : "test set size =" integer
  1328.  
  1329. test_table  : "test set =" table
  1330.  
  1331. table : num
  1332.       | table num
  1333.  
  1334. num : real
  1335.     | integer
  1336.  
  1337. largest : "largest =" largest_list
  1338.  
  1339. largest_list : num
  1340.              | largest_list num
  1341.  
  1342. smallest : "smallest =" smallest_list 
  1343.  
  1344. smallest_list : num
  1345.               | smallest_list num 
  1346.  
  1347. tree_spec : "tree" tree_statements
  1348.  
  1349. tree_statements : tree_statement
  1350.                 | tree_statements tree_statement
  1351.                 
  1352. tree_statement : tree_size
  1353.                | max_correct 
  1354.                | max_epochs
  1355.  
  1356. tree_size : "size =" integer
  1357.  
  1358. max_correct : "min correct =" integer
  1359.  
  1360. max_epochs : "max epochs =" integer
  1361.  
  1362.  
  1363.                                     -21-
  1364.  
  1365.  
  1366. 5     Other Demonstrations
  1367. ~     ~~~~~ ~~~~~~~~~~~~~~
  1368.  
  1369. In this section we briefly present some boolean function problems
  1370. which atrees have solved.
  1371.  
  1372.  
  1373. 5.1   The Multiplexor Problem
  1374.  
  1375. A multiplexor is a digital logic circuit which behaves as follows: there are
  1376. k input leads called control leads, and 2^k leads called the "other" input
  1377. leads.  If the input signals on the k control leads represent the number j
  1378. in binary arithmetic, then the output of the circuit is defined to be equal
  1379. to the value of the input signal on the jth one of the other leads (in some
  1380. fixed order).  A multiplexor is thus a boolean function of n = k + 2^k 
  1381. variables and is often referred to as an n-multiplexor.
  1382.  
  1383. Here is a program to define a multiplexor with three control leads, v[2],
  1384. v[1] and v[0], the fact that they are these particular variables being
  1385. irrelevant due to randomization in the programs:
  1386.  
  1387. /* Windows window procedure and initialization omitted for clarity */
  1388.  
  1389. /* An eleven input multiplexor function test */
  1390.  
  1391. #include <stdio.h>
  1392. #include <stdlib.h>
  1393. #include <windows.h>
  1394. #include "atree.h"
  1395.  
  1396. #define TRAINSETSIZE 2000
  1397. #define WIDTH 11
  1398. #define TESTSETSIZE 1000
  1399. #define TREESIZE 2047
  1400.  
  1401. char multiplexor(v)
  1402.  
  1403.      char *v;
  1404.  
  1405. {
  1406.       return(v[ v[2]*4 + v[1]*2 + v[0] + 3]);
  1407. }
  1408.  
  1409. main()
  1410. {
  1411.     int i;
  1412.     int j;
  1413.     LPBIT_VEC training_set;
  1414.     LPBIT_VEC icres;
  1415.     LPBIT_VEC test;
  1416.     char vec[WIDTH];
  1417.     char ui[1];
  1418.     int correct = 0;
  1419.     LPATREE tree;
  1420.     char szBuffer[80];
  1421.  
  1422.  
  1423.  
  1424.  
  1425.                                     -22-
  1426.  
  1427.  
  1428.     /* Initialise */
  1429.  
  1430.     training_set = (LPBIT_VEC) Malloc (TRAINSETSIZE * sizeof(bit_vec));
  1431.     MEMCHECK(training_set);
  1432.  
  1433.     icres = (LPBIT_VEC) Malloc (TRAINSETSIZE * sizeof(bit_vec));
  1434.     MEMCHECK(icres);
  1435.  
  1436.     atree_init();
  1437.  
  1438.     /* Create the test data */
  1439.  
  1440.     MessageBox(NULL, "Creating training data", "Multiplexor", MB_OK);
  1441.  
  1442.     for (i = 0; i < TRAINSETSIZE; i++)
  1443.     {
  1444.         for (j = 0; j < WIDTH; j++)
  1445.         {
  1446.             vec[j] = RANDOM(2);
  1447.         }
  1448.         training_set[i] = *(bv_pack(vec,WIDTH));
  1449.         ui[0] = multiplexor(vec);
  1450.         icres[i] = *(bv_pack(ui,1));
  1451.     }
  1452.  
  1453.     /* Create a tree and train it */
  1454.  
  1455.     MessageBox(NULL,"Training tree", "Multiplexor", MB_OK);
  1456.  
  1457.     tree = atree_create(WIDTH,TREESIZE);
  1458.     (void) atree_train(tree,training_set,icres,0,TRAINSETSIZE,
  1459.                        TRAINSETSIZE-1,100,1);
  1460.  
  1461.     /* Test the trained tree */
  1462.  
  1463.     MessageBox(NULL,"Testing the tree", "Multiplexor", MB_OK);
  1464.  
  1465.     for (i = 0; i < TESTSETSIZE; i++)
  1466.     {
  1467.         for (j = 0; j < WIDTH; j++)
  1468.         {
  1469.             vec[j] = RANDOM(2);
  1470.         }
  1471.         test = bv_pack(vec,WIDTH);
  1472.         if (atree_eval(tree,test) == multiplexor(vec))
  1473.         {
  1474.             correct++;
  1475.         }
  1476.         bv_free(test);
  1477.     }
  1478.  
  1479.     wsprintf(szBuff,"%d correct out of %d in final test",correct,TESTSETSIZE);
  1480.  
  1481.     /* discard training set */
  1482.     for (i = 0; i < TESTSETSIZE; i++)
  1483.         {
  1484.         Free(training_set[i].bv);
  1485.         Free(icres[i].bv);
  1486.         }
  1487.                                     -23-
  1488.  
  1489.  
  1490.     Free(training_set);
  1491.     Free(icres);
  1492.  
  1493.     /* Discard tree */
  1494.     atree_free(tree);
  1495.  
  1496.     return;
  1497. }
  1498.  
  1499. This problem was solved to produce a circuit testing correctly on 99.4\% of
  1500. 1000 test vectors in 19 epochs, or about 530 seconds on a Sun 3/50.  The
  1501. time may vary considerably depending on the random numbers used.  It is
  1502. possible to learn multiplexors with twenty inputs (four control leads) with
  1503. a straightforward but improved adaptation procedure, and multiplexors with
  1504. up to 521 leads (nine of them control leads) using much more elaborate
  1505. procedures which change the tree structure during learning [Arms5].
  1506.  
  1507.  
  1508. 5.2   The Mosquito Problem
  1509.  
  1510.  
  1511. Suppose we are conducting medical research on malaria, and we don't know yet
  1512. that malaria is caused by the bite of an anopheles mosquito, unless the
  1513. person is taking quinine (in Gin and Tonics, say) or has sickle-cell
  1514. anaemia.  We are inquiring into eighty boolean-valued factors of which
  1515. "bitten by anopheles mosquito", "drinks Gin and Tonics", and "has
  1516. sickle-cell anaemia" are just three.  For each of 500 persons in the sample,
  1517. we also determine whether or not the person has malaria, represented by
  1518. another boolean value, and we train a network on that data.  We then test
  1519. the learned function to see if it can predict, for a separately-chosen test
  1520. set, whether person whose data were not used in training has malaria.
  1521.  
  1522. Suppose on the test set, the result is 100% correct. (Training and test can 
  1523. be done in about five seconds on a Sun Sparcstation 1.)  Then it would be 
  1524. reasonable to analyse the function produced by the tree, and note all the 
  1525. variables among the eighty that are not involved in producing the result.  A 
  1526. complete data analysis system would have means of eliminating subtrees "cut 
  1527. off" by LEFT or RIGHT functions, to produce a simple function which would 
  1528. help the researcher understand some factors important for the presence of 
  1529. the disease.  If there were extraneous variables still left in the function
  1530. in one trial, perhaps they would not show up in a second trial, so that one
  1531. could see what variables are consistently important in drawing conclusions
  1532. about malaria.
  1533.  
  1534. We apologize for the simplistic example, however we feel the technique of
  1535. data analysis using these trees may be successful in cases where there are
  1536. complex interactions among features which tend to mask the true aetiology of
  1537. the disease.
  1538.  
  1539. The code for the problem can be found in mosquito.c.
  1540.  
  1541.  
  1542.  
  1543.  
  1544.  
  1545.  
  1546.  
  1547.  
  1548.  
  1549.                                    -24-
  1550.  
  1551.  
  1552. 6     References
  1553. ~     ~~~~~~~~~~
  1554.  
  1555. [Arms]  W. W. Armstrong, J. Gecsei: Adaptation Algorithms for
  1556.         Binary Tree Networks, IEEE Trans. on Systems, Man and Cybernetics,
  1557.         SMC-9 (1979), pp. 276 - 285.
  1558.  
  1559. [Arms2] W. W. Armstrong, Adaptive Boolean Logic Element, U. S.
  1560.         Patent 3,934,231, Jan. 20, 1976, assigned to Dendronic Decisions
  1561.         Limited.
  1562.  
  1563. [Arms3] W. W. Armstrong, J. Gecsei, Architecture of a Tree-Based
  1564.         Image Processor, 12th Asilomar Conf. on Circuits, Systems and
  1565.         Computers, IEEE Cat. No. 78CHI369-8 C/CAS/CS Nov. 1978, 345-349.
  1566.  
  1567. [Arms4] W. W. Armstrong, G. Godbout, Properties of binary trees
  1568.         of flexible elements useful in pattern recognition, Proc. IEEE Int'l.
  1569.         Conf. on Cybernetics and Society, San Francisco (1975) 447 - 450.
  1570.  
  1571. [Arms5] W. W. Armstrong, Jiandong Liang, Dekang Lin, Scott Reynolds,
  1572.         Experiments using Parsimonious Adaptive Logic, Technical Report
  1573.         TR 90-30, Department of Computing Science, University of Alberta,
  1574.         September 1990.
  1575.  
  1576. [Boch]  G. v. Bochmann, W. W. Armstrong: Properties of Boolean
  1577.         Functions with a Tree Decomposition, BIT 14 (1974), pp. 1 - 13.
  1578.  
  1579. [Hech]  Robert Hecht-Nielsen, Neurocomputing, Addison-Wesley,
  1580.         1990.
  1581.  
  1582. [Meis]  William S. Meisel, Parsimony in Neural Networks, Proc.
  1583.         IJCNN-90-WASH-DC, vol. I, pp. 443 - 446.
  1584.  
  1585. [Rume]  D. E. Rumelhart and J. L. McClelland: Parallel
  1586.         Distributed Processing, vols. 1&2, MIT Press, Cambridge, Mass. (1986).
  1587.  
  1588. [Smit]  Derek Smith, Paul Stanford: A Random Walk in Hamming
  1589.         Space, Proc. Int. Joint Conf. on Neural Networks, San Diego, vol. 2
  1590.         (1990) 465 - 470.
  1591.  
  1592.  
  1593.  
  1594. 7     Acknowledgements
  1595. ~     ~~~~~~~~~~~~~~~~
  1596.  
  1597. I'd like to thank Bill Armstrong for going through this document and
  1598. correcting it. His mark can be seen on some of my footnotes. Alas, there are
  1599. bound to be various errors and omissions still present, and for these, I
  1600. apologize in advance.
  1601.                                                         A. Dwelly
  1602.  
  1603. I'd like to thank Andy Dwelly for being patient with me while learning the
  1604. atree code, and for realizing that ports of 32 bit UNIX software
  1605. to a 16 bit segmented memory O/S merit having separate source files (death
  1606. to #ifdef WINDOWS).  Also, I apologize for any errors I have inadvertently 
  1607. propagated or created!
  1608.                                                         M. Thomas
  1609.  
  1610.  
  1611.                                     -25-
  1612.  
  1613.